<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
//构造器函数,当类被实例化的时候,实例化对象会直接调用原型对象上的constructor方法
        //第四步：封装defineReactive函数，主要是为了对单个数据做数据劫持（数据劫持的核心代码）
        
        function defineReactive(_data,key,value){ 
            //第五步：因为vm上的数据很有可能是属性的值又是一个对象，所以这种深层次的对象的属性也要做数据劫持，所以需要把value再一个传给observe
            observe(value)

            //把_data上原来的每一个数据，再使用defineProperty方法，这样每次获取时，都要进到get函数中，而不是直接拿到值
            Object.defineProperty(_data,key,{
                get(){
                    console.log("正在获取"+key+"的值")
                    return value
                
                },
                set(newVal){
                    //做一个简单的判断，减少重新赋值的操作
                if(value===newVal)  return
                 console.log("正在设置" + key + "的值");
                    value = newVal
                }
            
            })

        }

        //第三步：封装observe函数，遍历_data上的key，然后把key，_data,以及属性的值传给数据劫持函数，对每一个数据进行数据劫持
        function observe(_data){
            //第六步：因为参数_data既有可能是Vue中传进来的值，也有可能是defineProperty中传过来的值，所以要做判断,defineProperty中传过来的很可能是基本类型值或null，所以当为基本类型值或null时，就return，不用做数据劫持了
            if(typeof _data!=="object"||_data===null) return
            for(let key of Object.keys(_data)){
    //把每一个数据的响应式核心代码交给defineReactive函数处理
                defineReactive(_data,key,_data[key])
            }
            
        }

        //第二步，封装Vue构造函数，把实例化时传的数据对象赋值给实例的_data属性，在将_data中的数据在实例上进行数据代理一份，方便之后获取（就不用this._data.xxx获取了）数据代理之后，再把实例的_data属性传给observe函数调用进行响应式操作
       function Vue(options){
        // 构造函数的this指向实例化对象，所以指向vm
        // 将实例化Vue函数传的对象赋值给vm身上的_data属性
        
        console.log(this)
           this._data = options._data
        //    遍历_data的key，进行数据代理
            for(let key of Object.keys(this._data)){
                // 进行数据代理，把_data中的每一个key属性都给vm(this)一份
                //Object.defineProperty主要是为已经存在的对象添加属性,或者修改现有属性。
                //如果指定的属性名在对象中不存在,则执行添加操作，存在就是修改操作
                // 给vm上添加属性后，如果获取这个属性的值，就会触发Object.defineProperty第三个参数中的get方法，修改这个属性的值时，就会触发第三个参数的set方法，因为vm上的数据只是数据代理，所以在获取vm的数据时，需要在get方法中返回_data中的数据，在修改数据时，也是把新的值覆盖给_data中的数据
                Object.defineProperty(this,key,{
                    get(){
                         return this._data[key]
                    },
                    set(newVal){
                        this._data[key]=newVal
                    }              
                })            
            }

            //调用observe函数，并传入_data
            observe(this._data)
    
        }


        //第一步实例化vue，传入数据对象
        const vm =new Vue({
            _data:{
                name:"xiaogao",
                age:19,
                movies: [
                    { id: "001", movieName: "绿皮书" },
                    { id: "002", movieName: "迷雾" },
                ],
                wea: {
                    type: "下雨",
                    num: 28,
                },

            }
        })

        
        //第七步：封装一个获取函数和一个设置函数
        function huoqu(){
         console.log(vm.age)
        }
        function shezhi(){
            vm.age=20
        }

        //调用获取和设置函数，查看是否会进到defineReactive函数中的get和set中
        huoqu()
        // shezhi()
    </script>
</body>
</html>